<?php
/**
 * Created by PhpStorm
 * User: Jhinwy
 * Date: 5/21/24
 * Time: 11:47 AM
 */

namespace App\Services;

use App\Models\WechatAccountChat;
use App\Models\WechatAccountCustomer;
use App\Models\WechatDelCustomer;
use App\Models\WechatExtContactWelcome;
use App\Models\WechatKf;
use App\Models\WechatPush;
use App\Models\WechatTimingPush;
use App\Models\WechatUser;
use Carbon\Carbon;
use Cium\WeWorkApi\utils\Utils;
use App\Models\WechatCorp;
use App\Models\WechatKfDefault;
use App\Models\WechatKfKeyword;
use App\Models\WechatKfWelcome;
use GuzzleHttp\Client;
use GuzzleHttp\RequestOptions;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use Cium\WeWorkApi\callback\WXBizMsgCrypt;
use Illuminate\Support\Facades\Redis;


class KfAccountService
{
    const REDIS_CORP_WECHAT_REPLACE_ACCESS_TOKEN = 'corp_wechat_replace_access_token_';
    const REDIS_CORP_WECHAT_KF_MSG_CURSOR        = 'corp_wechat_kf_msg_cursor_';

    // 客户备注队列
    const REDIS_CORP_WECHAT_CUSTOMER_REMARK = 'corp_wecaht_customer_remark';

    public $headers = ['Content-Type' => 'application/json'];

    protected $suiteToken;
    protected $aesKey;


    public $api = [];

    public function __construct()
    {
        $this->suiteToken = env('CORP_WECHAT_SUITE_TOKEN', '');
        $this->aesKey     = env('CORP_WECHAT_ENCODING_AES_KEY', '');

        $this->api = config('corp_wechat');
    }


    /**
     * 数据/消息回调
     *
     * @param $msgSignature
     * @param $timestamp
     * @param $nonce
     * @param $echostr
     * @param string $contents
     * @param string $returnStr
     * @return int
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function dataCallback($msgSignature, $timestamp, $nonce, $echostr, $contents = '', &$returnStr = '')
    {
        Log::info('代开发企业微信消息回调', [$msgSignature, $timestamp, $nonce, $echostr, $contents]);

        $xmlData = Utils::Xml2Array($contents);
        $corpId  = $xmlData['ToUserName'] ?? '';
        if (!$corpId) {
            $corp   = WechatCorp::query()->orderBy('id', 'desc')->first();
            $corpId = $corp->corp_id ?? '';
        }
        $wxcpt = new WXBizMsgCrypt($this->suiteToken, $this->aesKey, $corpId);
        if ($contents) {
            $xmlContent = '';
            $errCode    = $wxcpt->DecryptMsg($msgSignature, $timestamp, $nonce, $contents, $xmlContent);
            if ($errCode == 0) {
                $data = Utils::Xml2Array($xmlContent);
                Log::info('代开发消息回调信息', [$data]);
                $event = $data['Event'] ?? '';
                switch ($event) {
                    case 'kf_msg_or_event' :
                        $openKfid   = $data['OpenKfId'] ?? '';
                        $nextCursor = Cache::get(self::REDIS_CORP_WECHAT_KF_MSG_CURSOR . $openKfid);
                        $msgList    = $this->syncMsg($corpId, $data['Token'] ?? '', $openKfid, $nextCursor);
                        foreach ($msgList as $item) {
                            $msgtype = $item['msgtype'] ?? '';
                            // 判断消息类型
                            switch ($msgtype) {
                                case 'event' :
                                    $event       = $item['event'] ?? [];
                                    $welcomeCode = $event['welcome_code'] ?? '';
                                    if ($welcomeCode) {   // 发送欢迎语
                                        $this->sendEventMsg($corpId, $openKfid, $welcomeCode);
                                    }
                                    break;
                                case 'text' :   // 匹配到客户文本消息（关键字等）
                                    $content         = $item['text']['content'] ?? '';
                                    $kfid            = $item['open_kfid'] ?? '';
                                    $external_userid = $item['external_userid'] ?? '';
                                    if ($content) {
                                        $this->sendMsg($corpId, $external_userid, $kfid, $content);
                                    }
                                    break;
                            }
                        }
                        break;
                    case 'change_external_chat' :
                        $changeType = $data['ChangeType'] ?? '';
                        switch ($changeType) {
                            case 'update' :
                                $chatId        = $data['ChatId'] ?? '';
                                $updateDetail  = $data['UpdateDetail'] ?? '';
                                $memChangeList = $data['MemChangeList']['Item'] ?? [];
                                if (!is_array($memChangeList)) {
                                    $memChangeList = [$memChangeList];
                                }
                                $userId = WechatAccountChat::query()->where('corp_id', $corpId)->where('chat_id', $chatId)->value('user_id');
                                if (($updateDetail == 'add_member') && $memChangeList && $userId) {
                                    foreach ($memChangeList as $item) {
                                        // 修改备注
                                        $remark = date('Ymd') . ' - 【已进群】';
                                        Redis::rpush(
                                            KfAccountService::REDIS_CORP_WECHAT_CUSTOMER_REMARK,
                                            json_encode([
                                                'corp_id'         => $corpId,
                                                'user_id'         => $userId,
                                                'external_userID' => $item,
                                                'remark'          => $remark
                                            ]));
                                    }
                                }
                                break;
                        }
                        break;
                    case 'change_external_contact' :
                        $changeType     = $data['ChangeType'] ?? '';
                        $userId         = $data['UserID'] ?? '';
                        $externalUserID = $data['ExternalUserID'] ?? '';
                        $createTime     = $data['CreateTime'] ?? '';
                        switch ($changeType) {
                            case 'add_external_contact' :   // 添加客户
                            case 'add_half_external_contact' :   // 添加客户
                                $welcomeCode = $data['WelcomeCode'] ?? '';
                                if ($welcomeCode) {
                                    $this->sendExternalcontactWelcome($welcomeCode, $corpId, $userId);
                                }
                                // 修改备注
                                $remark = date('Ymd') . ' - 【未进群】';
                                Redis::rpush(
                                    KfAccountService::REDIS_CORP_WECHAT_CUSTOMER_REMARK,
                                    json_encode([
                                        'corp_id'         => $corpId,
                                        'user_id'         => $userId,
                                        'external_userID' => $externalUserID,
                                        'remark'          => $remark
                                    ]));
                                break;
                            case 'del_external_contact' :   // 主动删除客户
                                WechatAccountCustomer::query()
                                    ->where('account_id', $userId)
                                    ->where('customer_id', $externalUserID)
                                    ->delete();
                                break;
                            case 'del_follow_user' :    // 被客户删除（修改备注，添加到单删库）
                                $delCustomer           = WechatDelCustomer::query()
                                    ->firstOrNew(['corp_id' => $corpId, 'user_id' => $userId, 'customer_id' => $externalUserID]);
                                $delCustomer->del_time = date('Y-m-d H:i:s', $createTime);
                                $delCustomer->save();

                                // 修改备注
                                $remark = date('Ymd') . ' - 【删】';
                                Redis::rpush(
                                    KfAccountService::REDIS_CORP_WECHAT_CUSTOMER_REMARK,
                                    json_encode([
                                        'corp_id'         => $corpId,
                                        'user_id'         => $userId,
                                        'external_userID' => $externalUserID,
                                        'remark'          => $remark
                                    ]));
                                break;
                        }
                        break;
                }
            }
        }

        $errCode = $wxcpt->VerifyURL($msgSignature, $timestamp, $nonce, $echostr, $returnStr);

        return $errCode;
    }


    /**
     * 修改客户备注
     *
     * @param $corpId
     * @param $userId
     * @param $externalUserid
     * @param $remark
     * @return bool
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function setCustomerRemark($corpId, $userId, $externalUserid, $remark)
    {
        if (!$remark) {

            return false;
        }
        try {
            $body = [
                'headers'            => $this->headers,
                RequestOptions::JSON => [
                    'userid'          => $userId,
                    'external_userid' => $externalUserid,
                    'remark'          => $remark
                ]
            ];
            Log::info('添加客户备注：', [$corpId, $userId, $externalUserid, $remark]);
            $client   = new Client();
            $response = $client->request(
                'POST',
                sprintf($this->api['externalcontact_remark'], $this->getReplaceAccessToken($corpId)),
                $body
            );
            if ($response->getStatusCode() != 200) {
                throw new \Exception('请求失败！');
            }
            $contents = $response->getBody()->getContents();
            Log::info('添加客户备注：', [$contents]);

            $data = json_decode($contents, true);
            if (($errcode = $data['errcode'] ?? -1) != 0) {
                throw new \Exception('修改失败：' . $errcode);
            }

            return true;
        } catch (\Exception $exception) {
            Log::error('修改客户备注失败：' . $exception->getMessage());

            return false;
        }
    }


    /**
     * 发送添加外部联系人欢迎语
     *
     * @param $welcomeCode
     * @param $corpId
     * @param $userId
     * @return bool|void
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function sendExternalcontactWelcome($welcomeCode, $corpId, $userId)
    {
        try {
            $welcome = WechatExtContactWelcome::query()
                ->where('corp_id', $corpId)
                ->where('user_id', $userId)
                ->first();
            if (!$welcome) {
                return;
            }
            $content     = $welcome->content;
            $attach      = $welcome->attachments ?? '';
            $attachments = json_decode($attach, true);


            $body = ['welcome_code' => $welcomeCode];
            if ($content) {
                $body['text'] = ['content' => $content];
            }
            if ($attachments) {
                $body['attachments'] = $attachments;
            }

            $client   = new Client();
            $response = $client->request(
                'POST',
                sprintf($this->api['externalcontact_welcome'], $this->getReplaceAccessToken($corpId)),
                [
                    'headers'            => $this->headers,
                    RequestOptions::JSON => $body
                ]
            );
            if ($response->getStatusCode() != 200) {
                throw new \Exception('请求失败！');
            }
            $contents = $response->getBody()->getContents();
            $data     = json_decode($contents, true);
            if (($errcode = $data['errcode'] ?? -1) != 0) {
                throw new \Exception($errcode);
            }
            Log::info('发送添加客户欢迎语===>', [$body, $response->getBody()->getContents()]);

            return true;
        } catch (\Exception $exception) {
            Log::error('发送添加客户欢迎语失败：' . $exception->getMessage());

            return false;
        }
    }


    /**
     * 同步客服消息
     *
     * @param $corpId
     * @param $token
     * @param $openKfid
     * @param string $cursor
     * @return array
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function syncMsg($corpId, $token, $openKfid, $cursor = '')
    {
        try {
            $body     = [
                'headers'            => $this->headers,
                RequestOptions::JSON => [
                    'cursor'       => $cursor,
                    'token'        => $token,
                    'limit'        => 100,
                    'voice_format' => 0,
                    'open_kfid'    => $openKfid,
                ]
            ];
            $client   = new Client();
            $response = $client->request(
                'POST',
                sprintf($this->api['sync_msg'], $this->getReplaceAccessToken($corpId)),
                $body
            );
            if ($response->getStatusCode() != 200) {
                throw new \Exception('请求失败！');
            }
            $contents = $response->getBody()->getContents();
            $data     = json_decode($contents, true);
            if (($errcode = $data['errcode'] ?? -1) != 0) {
                throw new \Exception($errcode);
            }
            $next_cursor = $data['next_cursor'] ?? '';
            // 缓存客服消息状态
            if ($next_cursor) {
                Cache::put(self::REDIS_CORP_WECHAT_KF_MSG_CURSOR . $openKfid, $next_cursor);
            }

            if (!$msgList = $data['msg_list'] ?? []) {

                return [];
            }

            return $msgList;
        } catch (\Exception $exception) {
            Log::error("同步客服消息{$corpId}失败：" . $exception->getMessage());

            return [];
        }
    }


    /**
     * 发送事件消息（欢迎语等..）
     *
     * @param $corpId
     * @param $msgId
     * @param string $code
     * @param string $msgType
     * @return bool
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function sendEventMsg($corpId, $openKfid, $code = '', $msgType = 'text')
    {
        try {
            $replayContent = WechatKfWelcome::query()
                ->where('corp_id', $corpId)
                ->where('open_kfid', $openKfid)
                ->orderBy('id', 'desc')
                ->first();
            $replayContent = $replayContent->content ?? '';
            // Log::info('发送客服欢迎语：', [$corpId, $openKfid, $code,$replayContent]);
            if (!$replayContent) {
                $default       = $this->getKfDefault($openKfid);
                $replayContent = $default->content;
            }
            Log::info('发送客服欢迎语：', [$corpId, $openKfid, $code, $replayContent]);
            $body     = [
                'headers'            => $this->headers,
                RequestOptions::JSON => [
                    'code'    => $code,
                    'msgtype' => $msgType,
                    $msgType  => [
                        'content' => $replayContent
                    ]
                ]
            ];
            $client   = new Client();
            $response = $client->request(
                'POST',
                sprintf($this->api['send_event_msg'], $this->getReplaceAccessToken($corpId)),
                $body
            );
            if ($response->getStatusCode() != 200) {
                throw new \Exception('请求失败！');
            }
            Log::info('发送欢迎语===>', [$response->getBody()->getContents()]);

            return true;
        } catch (\Exception $exception) {
            Log::error("发送客服欢迎语{$corpId} >>> {$openKfid}失败：" . $exception->getMessage());

            return false;
        }
    }


    /**
     * 发送消息
     *
     * @param $corpId
     * @param $msgId
     * @param string $code
     * @param string $msgType
     * @return bool
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function sendMsg($corpId, $toUser, $openkfid, $content)
    {
        try {
            $keywords = WechatKfKeyword::query()
                ->where('open_kfid', $openkfid)
                ->where('keyword', 'like', '%' . $content . '%')
                ->get();

            foreach ($keywords as $keyword) {
                Log::info('匹配消息：', [$corpId, $toUser, $openkfid, $content, $keyword, $keyword->msg_type]);

                switch ($keyword->msg_type) {
                    case WechatKfKeyword::MSG_TEXT :
                        $this->sendTextMsg($corpId, $toUser, $openkfid, $keyword->content);
                        break;
                    case WechatKfKeyword::MSG_IMAGE :
                        $this->sendImageMsg($corpId, $toUser, $openkfid, $keyword->content);
                        break;
                    case WechatKfKeyword::MSG_LINK:
                        $this->sendLinkMsg($corpId, $toUser, $openkfid, json_decode($keyword->content, true));
                        break;
                    case WechatKfKeyword::MSG_MINIPROGRAM:
                        $this->sendMiniProgramMsg($corpId, $toUser, $openkfid, json_decode($keyword->content, true));
                        break;
                }

                // 记录用户发送信息
                $this->saveWechatUser($corpId, $openkfid, $toUser, $content);
            }

            return true;
        } catch (\Exception $exception) {
            Log::error("发送客服消息{$corpId} >>> {$openkfid}失败：" . $exception->getMessage());

            return false;
        }
    }


    /**
     * 记录用户发送信息
     *
     * @param $corpId
     * @param $openkfid
     * @param $toUser
     * @return bool
     */
    public function saveWechatUser($corpId, $openkfid, $toUser, $content = '')
    {
        $wUser = WechatUser::query()
            ->firstOrNew(['open_kfid' => $openkfid, 'user_id' => $toUser]);

        $wUser->corp_id       = $corpId;
        $wUser->status        = WechatUser::STATUS_YES;
        $wUser->reply_at      = Carbon::now()->toDateTimeString();
        $wUser->reply_content = $content;

        return $wUser->save();
    }


    /**
     * 发送文字消息
     *
     * @param $corpId
     * @param $toUser
     * @param $openkfid
     * @param $content
     * @return bool
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function sendTextMsg($corpId, $toUser, $openkfid, $content)
    {
        try {
            $client = new Client();

            $body = [
                'headers'            => $this->headers,
                RequestOptions::JSON => [
                    'touser'    => $toUser,
                    'open_kfid' => $openkfid,
                    'msgtype'   => 'text',
                    'text'      => [
                        'content' => $content
                    ]
                ]
            ];

            $response = $client->request(
                'POST',
                sprintf($this->api['send_msg'], $this->getReplaceAccessToken($corpId)),
                $body
            );

            if ($response->getStatusCode() != 200) {
                throw new \Exception('请求失败！');
            }
            Log::info('发送文字消息===>', [$response->getBody()->getContents()]);

            return true;
        } catch (\Exception $exception) {
            Log::error('发送文字消息失败：' . $exception->getMessage());

            return false;
        }
    }


    /**
     * 发送图片消息
     *
     * @param $corpId
     * @param $toUser
     * @param $openkfid
     * @param $mediaId
     * @return bool
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function sendImageMsg($corpId, $toUser, $openkfid, $mediaId)
    {
        try {
            $client = new Client();

            $body = [
                'headers'            => $this->headers,
                RequestOptions::JSON => [
                    'touser'    => $toUser,
                    'open_kfid' => $openkfid,
                    'msgtype'   => 'image',
                    'image'     => [
                        'media_id' => $mediaId
                    ]
                ]
            ];

            $response = $client->request(
                'POST',
                sprintf($this->api['send_msg'], $this->getReplaceAccessToken($corpId)),
                $body
            );

            if ($response->getStatusCode() != 200) {
                throw new \Exception('请求失败！');
            }
            Log::info('发送图片消息===>', [$response->getBody()->getContents()]);

            return true;
        } catch (\Exception $exception) {
            Log::error('发送图片消息失败：' . $exception->getMessage());

            return false;
        }
    }


    /**
     * 发送图文消息
     *
     * @param $corpId
     * @param $toUser
     * @param $openkfid
     * @param $mediaId
     * @return bool
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function sendLinkMsg($corpId, $toUser, $openkfid, $content)
    {
        Log::info('发送图文消息', [$corpId, $toUser, $openkfid, $content]);

        try {
            $client = new Client();
            $body   = [
                'headers'            => $this->headers,
                RequestOptions::JSON => [
                    'touser'    => $toUser,
                    'open_kfid' => $openkfid,
                    'msgtype'   => 'link',
                    'link'      => [
                        'title'          => $content['title'],
                        'desc'           => $content['desc'],
                        'url'            => $content['url'],
                        'thumb_media_id' => $content['media_id']
                    ]
                ]
            ];

            $response = $client->request(
                'POST',
                sprintf($this->api['send_msg'], $this->getReplaceAccessToken($corpId)),
                $body
            );

            if ($response->getStatusCode() != 200) {
                throw new \Exception('请求失败！');
            }
            Log::info('发送图文消息===>', [$response->getBody()->getContents()]);

            return true;
        } catch (\Exception $exception) {
            Log::error('发送图文消息失败：' . $exception->getMessage());

            return false;
        }
    }


    /**
     * 发送小程序消息
     *
     * @param $corpId
     * @param $toUser
     * @param $openkfid
     * @param $mediaId
     * @return bool
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function sendMiniProgramMsg($corpId, $toUser, $openkfid, $content)
    {
        try {
            $client = new Client();

            $body = [
                'headers'            => $this->headers,
                RequestOptions::JSON => [
                    'touser'      => $toUser,
                    'open_kfid'   => $openkfid,
                    'msgtype'     => 'miniprogram',
                    'miniprogram' => [
                        'appid'          => $content['appid'],
                        'title'          => $content['title'],
                        'pagepath'       => $content['url'],
                        'thumb_media_id' => $content['media_id']
                    ]
                ]
            ];

            $response = $client->request(
                'POST',
                sprintf($this->api['send_msg'], $this->getReplaceAccessToken($corpId)),
                $body
            );

            if ($response->getStatusCode() != 200) {
                throw new \Exception('请求失败！');
            }
            Log::info('发送小程序消息===>', [$response->getBody()->getContents()]);

            return true;
        } catch (\Exception $exception) {
            Log::error('发送小程序消息失败：' . $exception->getMessage());

            return false;
        }
    }


    /**
     * 获取代开发应用授权token
     *
     * @param $corpId
     * @return bool|mixed|string
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function getReplaceAccessToken($corpId)
    {
        if ($accessToken = Cache::get(self::REDIS_CORP_WECHAT_REPLACE_ACCESS_TOKEN . $corpId)) {

            return $accessToken;
        }
        try {
            $corp = WechatCorp::query()->select('corp_id', 'permanent_code')->where('corp_id', $corpId)->first();
            Log::info('获取企业授权token:', [$corpId, $corp]);
            $client   = new Client();
            $response = $client->request(
                'GET',
                sprintf($this->api['replace_access_token'], $corpId, $corp->permanent_code),
                );
            if ($response->getStatusCode() != 200) {
                throw new \Exception('请求失败！');
            }
            $contents    = $response->getBody()->getContents();
            $data        = json_decode($contents, true);
            $accessToken = $data['access_token'] ?? '';
            $expiresIn   = $data['expires_in'] ?? 0;
            if (!$accessToken || !$expiresIn) {
                throw new \Exception('代开发应用token不存在');
            }
            Cache::put(self::REDIS_CORP_WECHAT_REPLACE_ACCESS_TOKEN . $corpId, $accessToken, $expiresIn);

            return $accessToken;
        } catch (\Exception $exception) {
            Log::error('获取代开发应用授权token失败：' . $exception->getMessage());

            return false;
        }
    }


    /**
     * 上传临时素材
     *
     * @param UploadedFile $file
     * @return bool|mixed
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function uploadImg($corpId, UploadedFile $file)
    {
        $filename = $file->getClientOriginalName();
        $body     = [
            RequestOptions:: MULTIPART => [
                [
                    'headers'  => [
                        'Content-Disposition' => 'form-data; name="image";filename="' . $filename . '"',
                        'Content-Type'        => 'image/png'
                    ],
                    'name'     => 'image',
                    'contents' => $file->getContent(),
                    'filename' => $filename
                ]
            ]
        ];
        try {
            $client   = new Client();
            $token    = $this->getReplaceAccessToken($corpId);
            $response = $client->request(
                'POST',
                sprintf($this->api['upload_img'], $token, 'image'),
                $body
            );
            if ($response->getStatusCode() != 200) {
                throw new \Exception('请求失败！');
            }
            $contents = $response->getBody()->getContents();
            $data     = json_decode($contents, true);
            if (($errcode = $data['errcode'] ?? -1) != 0) {
                throw new \Exception($errcode);
            }

            return $data;
        } catch (\Exception $exception) {
            Log::error('上传媒体素材失败：' . $exception->getMessage());

            return false;
        }
    }


    /**
     * 上传临时素材
     *
     * @param UploadedFile $file
     * @return bool|mixed
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function uploadMedia($corpId, UploadedFile $file)
    {
        $filename = $file->getClientOriginalName();
        $body     = [
            RequestOptions:: MULTIPART => [
                [
                    'headers'  => [
                        'Content-Disposition' => 'form-data; name="media";filename="' . $filename . '"; filelength=6',
                        'Content-Type'        => 'application/octet-stream'
                    ],
                    'name'     => 'media',
                    'contents' => $file->getContent(),
                    'filename' => $filename
                ]
            ]
        ];
        try {
            $client   = new Client();
            $token    = $this->getReplaceAccessToken($corpId);
            $response = $client->request(
                'POST',
                sprintf($this->api['upload_media'], $token, 'image'),
                $body
            );
            if ($response->getStatusCode() != 200) {
                throw new \Exception('请求失败！');
            }
            $contents = $response->getBody()->getContents();
            $data     = json_decode($contents, true);
            if (($errcode = $data['errcode'] ?? -1) != 0) {
                throw new \Exception($errcode);
            }

            return $data;
        } catch (\Exception $exception) {
            Log::error('上传媒体素材失败：' . $exception->getMessage());

            return false;
        }
    }


    /**
     * 获取临时素材
     *
     * @param $corpId
     * @param $mediaId
     * @return string
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function getMedia($corpId, $mediaId)
    {
        return sprintf($this->api['get_media'], $this->getReplaceAccessToken($corpId), $mediaId);
    }


    /**
     * 添加客服账号
     *
     * @param $params
     * @return bool
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function addKfAccount($params)
    {
        $name    = $params['name'] ?? '';
        $mediaId = $params['media_id'] ?? '';
        $corpId  = $params['corp_id'] ?? '';

        if (!$name || !$mediaId) {

            return false;
        }
        try {
            $body     = [
                'headers'            => $this->headers,
                RequestOptions::JSON => [
                    'name'     => $name,
                    'media_id' => $mediaId
                ]
            ];
            $client   = new Client();
            $response = $client->request(
                'POST',
                sprintf($this->api['add_account'], $this->getReplaceAccessToken($corpId)),
                $body
            );
            if ($response->getStatusCode() != 200) {
                throw new \Exception('请求失败！');
            }
            $contents = $response->getBody()->getContents();
            $data     = json_decode($contents, true);
            if (($errcode = $data['errcode'] ?? -1) != 0) {
                throw new \Exception('添加客服失败：' . $errcode);
            }

            if (!$openKfid = $data['open_kfid'] ?? '') {
                throw new \Exception('客服open id不存在');
            }
            $kf            = new WechatKf();
            $kf->corp_id   = $corpId;
            $kf->open_kfid = $openKfid;
            $kf->admin_id  = request()->admin->id;
            $kf->name      = $name;
            $kf->media_id  = $mediaId;
            $kf->save();

            return true;
        } catch (\Exception $exception) {
            Log::error('添加客服账号失败：' . $exception->getMessage());

            return false;
        }
    }


    /**
     * 获取企业微信客服账号列表
     *
     * @return array
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function getKfAccountList($cropId)
    {
        try {
            $body     = [
                'headers'            => $this->headers,
                RequestOptions::JSON => [
                    'offset' => 0,
                    'limit'  => 100
                ]
            ];
            $client   = new Client();
            $response = $client->request(
                'POST',
                sprintf($this->api['account_list'], $this->getReplaceAccessToken($cropId)),
                $body
            );
            if ($response->getStatusCode() != 200) {
                throw new \Exception('请求失败！');
            }
            $contents = $response->getBody()->getContents();
            $data     = json_decode($contents, true);
            Log::info('获取客服账号信息：', [$data]);
            if (($errcode = $data['errcode'] ?? -1) != 0) {
                throw new \Exception('获取列表失败：' . $errcode);
            }
            $list  = $data['account_list'] ?? [];
            $query = WechatKf::query();
            if (!UtilityService::isRoot()) {
                $query->where('admin_id', request()->admin->id);
            }
            $adminKfIds = $query->pluck('open_kfid')->toArray();

            foreach ($list as $key => &$item) {
                if (!$item['manage_privilege'] ?? false) {
                    unset($list[$key]);
                }
                if (!in_array($item['open_kfid'], $adminKfIds)) {
                    unset($list[$key]);
                }
            }

            return array_values($list);
        } catch (\Exception $exception) {
            Log::error('获取企业微信客服账号列表失败：' . $exception->getMessage());

            return [];
        }
    }


    /**
     * 删除企业微信客服账号
     *
     * @param $openKfid
     * @return bool
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function delKfAccount($corpId, $openKfid)
    {
        if (!$openKfid) {

            return false;
        }
        try {
            $body     = [
                'headers'            => $this->headers,
                RequestOptions::JSON => [
                    'open_kfid' => $openKfid,
                ]
            ];
            $client   = new Client();
            $response = $client->request(
                'POST',
                sprintf($this->api['del_account'], $this->getReplaceAccessToken($corpId)),
                $body
            );

            if ($response->getStatusCode() != 200) {
                throw new \Exception('请求失败！');
            }
            $contents = $response->getBody()->getContents();
            $data     = json_decode($contents, true);
            if (($errcode = $data['errcode'] ?? -1) != 0) {
                throw new \Exception($errcode);
            }
            $kf = WechatKf::query()->where('open_kfid', $openKfid)->first();
            $kf->delete();

            return true;
        } catch (\Exception $exception) {
            Log::error('删除客服账号失败：' . $exception->getMessage());

            return false;
        }
    }


    /**
     * 获取客服链接
     *
     * @param $corpId
     * @param $openKfid
     * @return bool|mixed
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function getKfLink($corpId, $openKfid)
    {
        if (!$openKfid) {

            return false;
        }
        try {
            $body     = [
                'headers'            => $this->headers,
                RequestOptions::JSON => [
                    'open_kfid' => $openKfid,
                ]
            ];
            $client   = new Client();
            $response = $client->request(
                'POST',
                sprintf($this->api['kf_link'], $this->getReplaceAccessToken($corpId)),
                $body
            );

            if ($response->getStatusCode() != 200) {
                throw new \Exception('请求失败！');
            }
            $contents = $response->getBody()->getContents();
            $data     = json_decode($contents, true);
            if (($errcode = $data['errcode'] ?? -1) != 0) {
                throw new \Exception($errcode);
            }

            return ['url' => $data['url'] ?? ''];
        } catch (\Exception $exception) {
            Log::error('获取客服链接失败：' . $exception->getMessage());

            return false;
        }
    }


    /**
     * 获取客服客户列表
     *
     * @param $params
     * @return array|\Illuminate\Contracts\Pagination\LengthAwarePaginator
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function getKfCustomerList($params)
    {
        $pageSize = $params['page_size'] ?? 20;
        $corpId   = $params['corp_id'] ?? '';
        $openKfid = $params['open_kfid'] ?? '';

        $query = WechatUser::query()
            ->where('corp_id', $corpId)
            ->where('open_kfid', $openKfid);

        $userIds = $query->pluck('user_id')->toArray();
        $list    = $query->orderBy('reply_at', 'desc')->paginate($pageSize);
        if ($userIds) {
            try {
                $client   = new Client();
                $token    = $this->getReplaceAccessToken($corpId);
                $body     = [
                    'headers'            => $this->headers,
                    RequestOptions::JSON => [
                        'external_userid_list' => $userIds
                    ]
                ];
                $response = $client->request(
                    'POST',
                    sprintf($this->api['customer_list'], $token),
                    $body
                );
                if ($response->getStatusCode() != 200) {
                    throw new \Exception('请求获取客户列表失败！');
                }
                $content      = $response->getBody()->getContents();
                $data         = json_decode($content, true);
                $customerData = [];
                if ($customerList = $data['customer_list'] ?? []) {
                    foreach ($customerList as $item) {
                        $customerData[$item['external_userid']] = [
                            'nickname' => $item['nickname'],
                            'avatar'   => $item['avatar'],
                            'gender'   => $item['gender']
                        ];
                    }
                }
                foreach ($list as &$item) {
                    $item->user_info = $customerData[$item->user_id] ?? [];
                }

                return $list;
            } catch (\Exception $exception) {
                Log::error('获取客户列表失败失败：' . $exception->getMessage());

                return [];
            }
        } else {

            return [];
        }
    }


    /**
     * 设置客服默认回复
     *
     * @param $params
     * @return bool
     */
    public function setKfDefault($params)
    {
        $corpId   = $params['corp_id'] ?? '';
        $openKfId = $params['open_kfid'] ?? '';
        $content  = $params['content'] ?? '';

        $welcome = WechatKfDefault::query()->firstOrNew(['corp_id' => $corpId, 'open_kfid' => $openKfId]);

        $welcome->content = $content;

        return $welcome->save();
    }


    /**
     * 获取客服默认回复
     *
     * @param $corpId
     * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model|object|null
     */
    public function getKfDefault($openKfid)
    {
        return WechatKfDefault::query()->where('open_kfid', $openKfid)->first();
    }


    /**
     * 设置客服消息
     *
     * @param $params
     * @return bool
     */
    public function setKfWelcome($params)
    {
        $corpId   = $params['corp_id'] ?? '';
        $openKfid = $params['open_kfid'] ?? '';
        $content  = $params['content'] ?? '';

        $welcome = WechatKfWelcome::query()->firstOrNew(['corp_id' => $corpId, 'open_kfid' => $openKfid]);

        $welcome->content = $content;

        return $welcome->save();
    }


    /**
     * 获取客服欢迎语
     *
     * @param $openKfid
     * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model|object|null
     */
    public function getKfWelcome($openKfid)
    {
        return WechatKfWelcome::query()->where('open_kfid', $openKfid)->first();
    }


    /**
     * 获取客服消息类型
     *
     * @return array
     */
    public function getKfMsgType()
    {
        return WechatKfKeyword::getMsgTypeMapping();
    }


    /**
     * 设置客服关键字
     *
     * @param $params
     * @return bool
     */
    public function setKfKeyword($params)
    {
        $id       = $params['id'] ?? '';
        $corpId   = $params['corp_id'] ?? '';
        $openKfid = $params['open_kfid'] ?? '';
        $msgType  = $params['msg_type'] ?? '';
        $keyword  = $params['keyword'] ?? '';
        $content  = $params['content'] ?? '';
        if (!$corpId || !$openKfid || !$msgType || !$keyword || !$content) {

            return false;
        }

        $wk = $id ? WechatKfKeyword::query()->find($id) : new WechatKfKeyword();

        $wk->corp_id   = $corpId;
        $wk->open_kfid = $openKfid;
        $wk->msg_type  = $msgType;
        $wk->keyword   = $keyword;
        $wk->content   = in_array($msgType, [WechatKfKeyword::MSG_LINK, WechatKfKeyword::MSG_MINIPROGRAM]) ?
            json_encode($content, JSON_UNESCAPED_UNICODE) : $content;

        return $wk->save();
    }


    /**
     * 获取客服关键字
     *
     * @param $openKfid
     * @return \Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection
     */
    public function getKfKeyword($openKfid)
    {
        $list = WechatKfKeyword::query()->where('open_kfid', $openKfid)->get();
        foreach ($list as &$item) {
            if ($item->msg_type == WechatKfKeyword::MSG_LINK) {
                $item->content   = json_decode($item->content, true);
                $item->media_url = $this->getMedia($item->corp_id, $item->content['media_id']);
            }
            if ($item->msg_type == WechatKfKeyword::MSG_IMAGE) {
                $item->media_url = $this->getMedia($item->corp_id, $item->content);
            }
            if ($item->msg_type == WechatKfKeyword::MSG_MINIPROGRAM) {
                $item->content   = json_decode($item->content, true);
                $item->media_url = $this->getMedia($item->corp_id, $item->content['media_id']);
            }
        }

        return $list;
    }


    /**
     * 删除客服关键字
     *
     * @param $id
     * @return mixed
     */
    public function delKfKeyword($id)
    {
        return WechatKfKeyword::query()->where('id', $id)->delete();
    }


    /**
     * 获取推送间隔时间
     *
     * @return array|mixed
     */
    public function getPushTime()
    {
        return WechatPush::$pushTimeMapping;
    }


    /**
     * 设置客服推送
     *
     * @param $params
     * @return bool
     */
    public function setKfPush($params)
    {
        $id       = $params['id'] ?? '';
        $corpId   = $params['corp_id'] ?? '';
        $openKfid = $params['open_kfid'] ?? '';
        $pushTime = $params['push_time'] ?? 0;
        $msgType  = $params['msg_type'] ?? '';
        $content  = $params['content'] ?? '';
        $title    = $params['title'] ?? '';
        $desc     = $params['desc'] ?? '';
        $url      = $params['url'] ?? '';
        $mediaId  = $params['media_id'] ?? '';
        $appid    = $params['appid'] ?? '';
        if (!$corpId || !$openKfid || !$msgType) {

            return false;
        }

        $push = $id ? WechatPush::query()->find($id) : new WechatPush();

        $push->corp_id   = $corpId;
        $push->open_kfid = $openKfid;
        $push->push_time = $pushTime;
        $push->msg_type  = $msgType;
        $push->content   = $content;
        $push->title     = $title;
        $push->desc      = $desc;
        $push->url       = $url;
        $push->media_id  = $mediaId;
        $push->appid     = $appid;

        return $push->save();
    }


    /**
     * 获取客服推送消息
     *
     * @param $corpId
     * @return \Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection
     */
    public function getKfPush($corpId, $openKfid)
    {
        $list = WechatPush::query()->where([
            'corp_id'   => $corpId,
            'open_kfid' => $openKfid
        ])->get();

        foreach ($list as &$item) {
            if (in_array($item->msg_type, [WechatKfKeyword::MSG_LINK, WechatKfKeyword::MSG_IMAGE, WechatKfKeyword::MSG_MINIPROGRAM])) {
                $item->media_url = $this->getMedia($item->corp_id, $item->media_id);
            }
        }

        return $list;
    }


    /**
     * 删除客服推送
     *
     * @param $id
     * @return mixed
     */
    public function delKfPush($id)
    {
        return WechatPush::query()->where('id', $id)->delete();
    }


    /**
     * 设置客服定时推送
     *
     * @param $params
     * @return bool
     */
    public function setKfTimingPush($params)
    {
        $id       = $params['id'] ?? '';
        $corpId   = $params['corp_id'] ?? '';
        $openKfid = $params['open_kfid'] ?? '';
        $pushTime = $params['push_time'] ?? 0;
        $msgType  = $params['msg_type'] ?? '';
        $content  = $params['content'] ?? '';
        $title    = $params['title'] ?? '';
        $desc     = $params['desc'] ?? '';
        $url      = $params['url'] ?? '';
        $mediaId  = $params['media_id'] ?? '';
        $appid    = $params['appid'] ?? '';
        if (!$corpId || !$openKfid || !$msgType) {

            return false;
        }

        $push = $id ? WechatTimingPush::query()->find($id) : new WechatTimingPush();

        $push->corp_id   = $corpId;
        $push->open_kfid = $openKfid;
        $push->push_time = $pushTime;
        $push->msg_type  = $msgType;
        $push->content   = $content;
        $push->title     = $title;
        $push->desc      = $desc;
        $push->url       = $url;
        $push->media_id  = $mediaId;
        $push->appid     = $appid;

        return $push->save();
    }


    /**
     * 获取客服定时推送消息
     *
     * @param $corpId
     * @return \Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection
     */
    public function getKfTimingPush($corpId, $openKfid)
    {
        $list = WechatTimingPush::query()->where([
            'corp_id'   => $corpId,
            'open_kfid' => $openKfid
        ])->get();

        foreach ($list as &$item) {
            if (in_array($item->msg_type, [WechatKfKeyword::MSG_LINK, WechatKfKeyword::MSG_IMAGE, WechatKfKeyword::MSG_MINIPROGRAM])) {
                $item->media_url = $this->getMedia($item->corp_id, $item->media_id);
            }
        }

        return $list;
    }


    /**
     * 删除客服定时推送
     *
     * @param $id
     * @return mixed
     */
    public function delKfTimingPush($id)
    {
        return WechatTimingPush::query()->where('id', $id)->delete();
    }
}
